﻿//-----------------------------------------------------------------------------------------------------------
// Wbfs Engine (WBFSe) Project by Omega Frost 
// http://wbfse.codeplex.com/
//
// WBFSe is Licensed under the terms of the 
// Microsoft Reciprocal License (Ms-RL)
//-----------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using WBFSe.Data;
using WBFSe.IO;
using WBFSe.Attributes;

namespace WBFSe
{
    /// <summary>
    /// Indexing and search
    /// </summary>
    static class WbfsIndexing
    {
        // Variables

        private static ReaderWriterLockSlim Lock = 
            new ReaderWriterLockSlim();

        private static IndexNode Root = 
            new IndexNode(".");

        private static List<IWbfsFileInfo> List =
            new List<IWbfsFileInfo>();

        // Methods

        /// <summary>
        /// Adds a file to the index
        /// </summary>
        /// <param name="file">File</param>
        internal static void AddFile(IWbfsFileInfo file)
        {
            String[] path = file.Path.Split(new char[] { '\\', '/' },
                StringSplitOptions.RemoveEmptyEntries);

            Lock.EnterWriteLock();

            IndexNode node = Root;
            foreach (String p in path)
            {
                IndexNode next = node.GetNode(p);
                if (next == null) next = node.AddNode(p);
                node = next;
            }

            node.AddFile(file);

            List.Add(file);
            
            Lock.ExitWriteLock();
        }

        /// <summary>
        /// Removes a file from the index
        /// </summary>
        /// <param name="file">File</param>
        internal static void RemoveFile(IWbfsFileInfo file)
        {
            String[] path = file.Path.Split(new char[] { '\\', '/' },
                StringSplitOptions.RemoveEmptyEntries);

            Lock.EnterWriteLock();

            IndexNode node = Root;
            foreach (String p in path)
            {
                node = node.GetNode(p);
                if (node == null)
                {
                    throw new ArgumentException("The file is not indexed");
                }
            }

            node.RemoveFile(file);

            List.Remove(file);

            Lock.ExitWriteLock();
        }

        /// <summary>
        /// Retrieves the wbfs file corresponding to the filename
        /// </summary>
        /// <param name="path">The location of the file</param>
        /// <param name="filename">The filename of the container</param>
        /// <returns>The file or null</returns>
        internal static IWbfsFileInfo GetFile(String path, String filename)
        {
            path = System.IO.Path.GetFullPath(path);

            String[] spath = path.Split(new char[] { '\\', '/' },
                StringSplitOptions.RemoveEmptyEntries);

            Lock.EnterReadLock();

            IndexNode node = Root;
            foreach (String p in spath)
            {
                node = node.GetNode(p);
                if (node == null)
                {
                    Lock.ExitReadLock();
                    return null;
                }
            }

            IWbfsFileInfo f = node.GetFile(filename);

            Lock.ExitReadLock();
            return f;
        }

        /// <summary>
        /// Searches for matches in the index
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="mode">Search mode</param>
        /// <returns>List of matches</returns>
        internal static List<IWbfsFileInfo> FindFiles(String key, SearchMode mode)
        {
            Lock.EnterReadLock();

            bool searchName = mode.HasFlag(SearchMode.Name);
            bool searchCode = mode.HasFlag(SearchMode.Code);
            bool searchPath = mode.HasFlag(SearchMode.Path);

            List<IWbfsFileInfo> found = new List<IWbfsFileInfo>();
            foreach (IWbfsFileInfo file in List)
            {
                if (searchName && (file.Name.IndexOf(key, StringComparison.
                    CurrentCultureIgnoreCase) >= 0))
                {
                    found.Add(file);
                    continue;
                }

                if (searchCode && (file.Name.IndexOf(key, StringComparison.
                    CurrentCultureIgnoreCase) >= 0))
                {
                    found.Add(file);
                    continue;
                }

                if (searchPath)
                {
                    if (file.Path.IndexOf(key, StringComparison.
                            CurrentCultureIgnoreCase) >= 0)
                    {
                        found.Add(file);
                        continue;
                    }

                    foreach (String filename in file.Filenames)
                    {
                        if (filename.IndexOf(key, StringComparison.
                            CurrentCultureIgnoreCase) >= 0)
                        {
                            found.Add(file);
                            break;
                        }
                    }
                }
            }

            Lock.ExitReadLock();
            return found;
        }
    }
}
